package collection;

public class FlexibleArray<E> {
	
	Object[] array = new Object[]{null};
	int size = 0;
	
	public FlexibleArray(){}
	
	public FlexibleArray( int size )
	{
		this.array = new Object[size];
		for(int i=0; i < this.array.length; i++)
		{
			array[i] = null;
		}
	}
	
	private void checkIfOutOfBounds( int index )
	{
		if ( index < 0 || index >= this.size )
		{
			throw new IndexOutOfBoundsException();
		}
	}
	
	private void checkIfOutOfBoundsForAdd( int index )
	{
		if ( index < 0 || index > this.size )
		{
			throw new IndexOutOfBoundsException();
		}
	}
	
	public boolean isEmpty()
	{
		return this.size == 0;
	}
	
	public boolean isFull()
	{
		return this.size == this.array.length;
	}
	
	public boolean isDense()
	{
		return this.array.length < this.size * 2;
	}
	
	public E get( int index )
	{
		checkIfOutOfBounds(index);
		return (E) this.array[index];
	}
	
	public void set( int index, E element )
	{
		checkIfOutOfBounds(index);
		this.array[index] = element;
	}
	
	public void switchToArrayOfSize( int newSize )
	{
		Object[] newArray = new Object[newSize];
		for (int i=0; i<newArray.length; i++)
		{
			if (i<this.size) {
				newArray[i] = this.array[i];
			}
			else
			{
				newArray[i] = null;
			}
		}
		this.array = newArray;
	}

	public void expand()
	{
		switchToArrayOfSize( this.array.length * 2 );
	}
	
	public void reduce()
	{
		switchToArrayOfSize( this.array.length / 2 );
	}
	
	public void add( int index, E element )
	{	
		checkIfOutOfBoundsForAdd( index );
		if (!isFull()) 
		{
			for (int i = this.size - 1; i >= index; i--) 
			{
				this.array[i + 1] = this.array[i];
			}
			this.array[index] = element;
			this.size += 1;
		}
		else
		{
			expand();
			add(index, element);
		}
	}
	
	public void add( E element )
	{
		add(this.size, element);
	}
	
	public E remove( int index )
	{
		checkIfOutOfBounds( index );
		if (isDense()) 
		{
			E retElement = get(index);
			for (int i = index; i < this.size - 1; i++) 
			{
				this.array[i] = this.array[i + 1];
			}
			this.array[size - 1] = null;
			this.size -= 1;
			return retElement;
		}
		else
		{
			reduce();
			return remove(index);
		}
	}
	
	public E remove()
	{
		return remove(this.size-1);
	}
	
	public void clear()
	{
		this.array = new Object[]{null};
		this.size = 0;
	}
	
	public int size()
	{
		return this.size;
	}
	
	@Override
	public String toString()
	{
		if (isEmpty())
			return "[]";
		
		String retStr = "[ ";
		for (int i=0; i<this.size ; i++)
		{
			if (i == this.size-1)
				retStr += this.array[i];
			else
				retStr += this.array[i] + ", ";
		}
		retStr += " ]";
		
		return retStr;
	}
}
